package me.spring.cloud.common.components.util.net;

import cn.hutool.core.net.NetUtil;
import cn.hutool.json.JSONUtil;
import me.spring.cloud.common.components.util.http.HttpReqUtil;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.spring.cloud.common.components.util.lang.StringUtil;

/**
 * @author luffy
 * @description: IP验证工具类
 * @date 2019-05-09 17:35
 */
public class IpUtil extends NetUtil {

  private static final String ipV4_regex = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$";
  private static final Pattern ipV4_Pattern = Pattern.compile(ipV4_regex);

  /**
   * @Author luffy
   * @Description 获取请求主机IP地址, 如果通过代理进来，则透过防火墙获取真实IP地址;
   * @Param request
   * @Return java.lang.String
   * @Date 2019-09-05 09:14
   */
  public static String getFirstIpByRequest(HttpServletRequest request) {
    String[] ips = getIpByRequest(request);

    if (ips.length == 1) {
      return ips[0];
    } else {
      return getIp(ips);
    }
  }

  /**
   * @Author luffy
   * @Description 获取第一个ip
   * @Param ips
   * @Return java.lang.String
   * @Date 2019-09-05 13:21
   */
  public static String getIp(String[] ips) {
    Optional<String> optional = Arrays.stream(ips)
        .filter(ip -> !StringUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip)).findFirst();
    return optional.isPresent() ? optional.get() : null;
  }


  /**
   * @Author luffy
   * @Description 获取所有内网ip
   * @Param ips
   * @Return java.lang.String[]
   * @Date 2019-09-05 13:24
   */
  public static String[] getInnerIp(String[] ips) {
    return (String[]) Arrays.stream(ips).filter(NetUtil::isInnerIP).toArray();
  }

  /**
   * @Author luffy
   * @Description 获取所有外网ip
   * @Param ips
   * @Return java.lang.String[]
   * @Date 2019-09-05 13:25
   */
  public static String[] getOuterIp(String[] ips) {
    return (String[]) Arrays.stream(ips).filter(ip -> !NetUtil.isInnerIP(ip)).toArray();
  }

  /**
   * @Author luffy
   * @Description 获取ip数量
   * @Param request
   * @Return int
   * @Date 2019-09-05 09:15
   */
  public static int getIpNumByRequest(HttpServletRequest request) {
    return getIpByRequest(request).length;
  }

  /**
   * @Author luffy
   * @Description 内网ip数量
   * @Param request
   * @Return int
   * @Date 2019-09-05 09:20
   */
  public static int getInnerIpNum(HttpServletRequest request) {
    return (int) Arrays.stream(getIpByRequest(request)).filter(NetUtil::isInnerIP).count();
  }

  /**
   * @Author luffy
   * @Description 内网ip数量
   * @Param ips
   * @Return int
   * @Date 2019-09-05 09:20
   */
  public static int getInnerIpNum(String[] ips) {
    return (int) Arrays.stream(ips).filter(NetUtil::isInnerIP).count();
  }

  /**
   * @Author luffy
   * @Description 外网数量
   * @Param request
   * @Return int
   * @Date 2019-09-05 09:22
   */
  public static int getOuterIpNum(HttpServletRequest request) {
    return (int) Arrays.stream(getIpByRequest(request)).filter(ip -> !NetUtil.isInnerIP(ip))
        .count();
  }

  /**
   * @Author luffy
   * @Description 外网数量
   * @Param ips
   * @Return int
   * @Date 2019-09-05 09:23
   */
  public static int getOuterIpNum(String[] ips) {
    return (int) Arrays.stream(ips).filter(ip -> !NetUtil.isInnerIP(ip)).count();
  }

  /**
   * @Author luffy
   * @Description 获取请求中所有ip
   * @Param request
   * @Return java.lang.String[]
   * @Date 2019-09-05 09:23
   */
  public static String[] getIpByRequest(HttpServletRequest request) {
    //获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址
    String ip = request.getHeader("X-Forwarded-For");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
      if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("Proxy-Client-IP");
      }
      if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("WL-Proxy-Client-IP");
      }
      if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("HTTP_CLIENT_IP");
      }
      if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
      }
      if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
      }
    } else if (ip.length() > 15) {
      return ip.split(",");
    }
    return new String[]{ip};
  }

  /**
   * @Author luffy
   * @Description 获取ip位置信息
   * @Param ip
   * @Return java.lang.String
   * @Date 2019-09-05 09:25
   */
  public static IpAddress getIpAddress(String ip) {
    Map<String, String> map = new HashMap<>();
    map.put("Origin", "https://ipgeolocation.io");
    map.put("Referer", "https://ipgeolocation.io/");
    map.put("Sec-Fetch-Mode", "cors");
    return JSONUtil.toBean(
        HttpReqUtil
            .getResponseForRdmIp("https://api.ipgeolocation.io/ipgeo?include=hostname&ip=" + ip,
                map, 1000)
            .getResponse().body(), IpAddress.class);
  }

  /**
   * @Author luffy
   * @Description 是否是外网ip
   * @Param ip
   * @Return boolean
   * @Date 2019-09-16 17:26
   */
  public static boolean isOutIP(String ip) {
    return ipV4_Pattern.matcher(ip).find() && !NetUtil.isInnerIP(ip);
  }

  /**
   * @Author luffy
   * @Description 判断是否是ip
   * @Param ip
   * @Return boolean
   * @Date 2019-09-20 11:43
   */
  public static boolean isIp(String ip) {
    return ipV4_Pattern.matcher(ip).find();
  }


  /**
   * @author luffy
   * @description: ip地址信息
   * @date 2019-05-09 17:45
   */
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  public class IpAddress implements Serializable {

    private String ip;
    private String hostname;
    private String continent_code;
    private String continent_name;
    private String country_code2;
    private String country_code3;
    private String country_name;
    private String country_capital;
    private String state_prov;
    private String district;
    private String city;
    private String zipcode;
    private String latitude;
    private String longitude;
    private String is_eu;
    private String calling_code;
    private String country_tld;
    private String languages;
    private String country_flag;
    private String geoname_id;
    private String isp;
    private String connection_type;
    private String organization;
    private String asn;
  }
}
